﻿import com.mosesSupposes.fuse.FuseKitCommon;

import flash.filters.BevelFilter;
import flash.filters.BitmapFilter;
import flash.filters.BlurFilter;
import flash.filters.ColorMatrixFilter;
import flash.filters.ConvolutionFilter;
import flash.filters.DisplacementMapFilter;
import flash.filters.DropShadowFilter;
import flash.filters.GlowFilter;
import flash.filters.GradientBevelFilter;
import flash.filters.GradientGlowFilter;

/**
* Fuse Kit 2
* Copyright (c) 2006 Moses Gunesch, MosesSupposes.com
* 
* Distributed under MIT Open Source License, see Fuse-Kit-License.html (in fuse package directory)
* Easing Equations (c) 2003 Robert Penner used by permission, see PennerEasing
* Visit http://www.mosessupposes.com/Fuse
* 
* @ignore
* 
* Provides easy Flash8 BitmapFilter handling. Can be used as a standalone utility or in conjunction with ZigoEngine & Fuse to animate filters.<br>
* <br>
* @usage
* <pre>// 1. normal
* FuseFMP.writeFilter(clip1, "DropShadow"); 
* FuseFMP.setFilterProp(box, "DropShadow_color", 0x336699);
* 
* // 2. Shortcuts registered with ZigoEngine 
* //    (or FuseFMP.simpleSetup called if using without ZigoEngine)
* 
* clip1.writeFilter("DropShadow");
* clip1.DropShadow_color = "#336699"; // supports strings for color
* clip1.DropShadow_blur = 10; // sets blurX & blurY at once
* 
* </pre>
* @description
* FuseFMP uses a unique naming convention for addressing BitmapFilter properties:
* <br><br>
* <i><b>short-filtername+"_"+filterprop</b></i>
* <br>
* <code>DropShadow_distance</code><br>
* <code>Bevel_angle</code><br>
* etc.
* <br>
* <br>
* Such pseudonyms are used in various management methods like {@link #setFilterProp}.<br>
* <br><br>
* Special Blur handling:<br>
* <ul><li>An extra <code>_blur</code> suffix makes it possible to set blurX and blurY at once:  
* <code>Blur_blur, Glow_blur,</code> etc.</li>
* <li>The class property FuseFMP.{@link #BLUR_ZERO}, active by default, sets the blurX and blurY properties of 
* any new BlurFilter generated by FuseFMP to 0 instead of the standard 4. This reduces code for animations.</li>
* </ul>
* <br>
* For a complete list of FuseFMP properties call FuseFMP.{@link #getAllShortcuts} or FuseFMP.{@link #traceAllFilters}.<br>
* <br><br>
* FuseFMP also supports its own optional Shortcuts functionality, which writes all of these pseudo-props into targets or prototypes. 
* This enables the getting and setting of BitmapFilter properties directly on targets, such as <code>my_mc.Blur_blurX = 10;</code> and, 
* if using ZigoEngine, tween shortcuts like <code>my_mc.Blur_blurXTo(10);</code>.
* <br><br>
* For a complete list of FuseFMP tweening methods see {@link com.mosesSupposes.fuse.Shortcuts} documentation.<br>
* If you're using FuseFMP without ZigoEngine and want to extend prototypes use {@link #simpleSetup}, or if you want to add & remove 
* shortcuts on specific targets use {@link #initialize} & {@link #deinitialize}.<br>
* <br>
* FuseFMP pseudo-properties are also directly tweenable in ZigoEngine and Fuse once registered:<br>
* <pre>// tween the dropshadow angle 25 degrees ccw, relative to current setting:
* ZigoEngine.doTween(my_mc, "DropShadow_angle", "-25");
* 
* // this fuse action tweens a horizontal blur back to the clip's normal state 
* Fuse.push({ start_Blur_blurX:100 });</pre>
* <br><br><br>
* <b>BitmapFilter tips:</b>
* <br><br>
* <ul>
* <li>Remember that alpha is a 0-1 multiplier, not a 0-100 percentage.</li>
* <li>When building complex filters like GradientBevel_color and GradientGlow_color, you should apply the colors Array 
* prior to the ratios array. The ZigoEngine has been updated to handle tweening these arrays as normal properties.</li>
* <li>When creating Fuses you can strip filters from a target once they're done being used with a simple action like: 
* <code>{ scope:FuseFMP, func:"removeFilter", args:"Blur" }</code></li>
* </ul>
* @author	Moses Gunesch / MosesSupposes.com / Based on FMP 1.0 by Danilo Sandner & Bjorn Wibben
* @version	2.1.3r1
*/
class com.mosesSupposes.fuse.FuseFMP {
	
	/**
	 * @exclude
	 * Unique identifier used by ZigoEngine.register
	 */ 
	public static var registryKey:String = 'fuseFMP';
	
	/**
	 * Enables kit version to be retrieved at runtime or when reviewing a decompiled swf. 
	 * @usage <pre>trace(FuseFMP.VERSION); // if the version is incorrect, clear your ASO cache.</pre>
	 */
	public static var VERSION:String = FuseKitCommon.VERSION;
	
	/**
	 * When true, generated BlurFilters are set to 0 blurX & blurY until manually set.
	 * @description 
	 * FuseFMP always creates new BitmapFilters with default properties, but BlurFilter has been found to 
	 * be a special case in that the default blur of 4 frequently requires extra code to undo back to 0. 
	 * <br><br>
	 * For example: (Assuming no blur has been applied in advance) without BLUR_ZERO, the Fuse action 
	 * <code>{start_Blur_blurX:100}</code> results in a Blur_blurY of 4 and a blurX tween from 100 to 4, 
	 * leaving the target slightly fuzzy. With BLUR_ZERO turned on the same action would show only the blurX 
	 * tween from 100 to 0 with the target ending up looking crisp.
	 */
	public static var BLUR_ZERO:Boolean = true;
	
	/**
	* Extends MC, Button & TF prototypes with FuseFMP shortcuts.
	* @description This enables the getting and setting of BitmapFilter properties directly on targets, using the convention short-filtername+"_"+filterprop.
	* <br><br>For a complete list of shortcut properties call FuseFMP.{@link #getAllShortcuts} or FuseFMP.{@link #traceAllFilters}.<br><br>
	* Also sets _global references to FuseFMP and all filter classes so timeline coders can avoid using import statements.<br><br>
	* Note that you do not need to call this method if you're using {@link com.mosesSupposes.fuse.ZigoEngine#simpleSetup}, it will be called automatically.
	* @see com.mosesSupposes.fuse.ZigoEngine#simpleSetup
	* @see com.mosesSupposes.fuse.ZigoEngine#register
	*/
	public static function simpleSetup() : Void {
		initialize(MovieClip.prototype, Button.prototype, TextField.prototype);
		_global.FuseFMP = FuseFMP;
		for (var i:String in _classes) {
			_global[i] = _classes[i];
		}
	}
	
	/**
	 * Internal; Ensures import
	 */ 
	private static var _classes:Object;
	/**
	 * Internal; memory object for shortcut methods
	 */ 
	private static var _shortcuts:Object;
	/**
	 * Internal; resolve object for shortcut getters
	 */ 
	private static var _getter:Object;
	/**
	 * Internal; resolve object for shortcut setters
	 */ 
	private static var _setter:Object;
	
	/**
	* Adds shortcuts to all target objects passed.
	* @description This enables the getting and setting of BitmapFilter properties directly on targets, 
	* using the FuseFMP naming convention [short-filtername+"_"+filterprop]. Like, 'DropShadow_angle'.
	* <br><br>
	* For a complete list of shortcut properties call FuseFMP.{@link #getAllShortcuts} or FuseFMP.{@link #traceAllFilters}.<br><br>
	* @param	Accepts one or more MovieClips, TextFields, or Buttons to initialize with shortcuts.
	* @see		#deinitialize
	*/
	public static function initialize(target:Object):Void {
		if (_classes == undefined) {
			_shortcuts = {getFilterName:function (f:BitmapFilter):String {
				return FuseFMP.getFilterName(f);
			}, getFilterIndex:function (f:Object):Number {
				return FuseFMP.getFilterIndex(this, f);
			}, getFilter:function (f:Object, createNew:Boolean):BitmapFilter {
				return FuseFMP.getFilter(this, f, createNew);
			}, writeFilter:function (f:Object, pObj:Object):Number {
				return FuseFMP.writeFilter(this, f, pObj);
			}, removeFilter:function (f:Object):Boolean {
				return FuseFMP.removeFilter(this, f);
			}, getFilterProp:function (prop:String, createNew:Boolean) {
				return FuseFMP.getFilterProp(this, prop, createNew);
			}, setFilterProp:function (prop:String, v:Object):Void {
				FuseFMP.setFilterProp(this, prop, v);
			}, setFilterProps:function (fOrPObj:Object, pObj:Object):Void {
				FuseFMP.setFilterProps(this, fOrPObj, pObj);
			}, traceAllFilters:function ():Void {
				FuseFMP.traceAllFilters();
			}};
			_classes = {BevelFilter:BevelFilter, BlurFilter:BlurFilter, ColorMatrixFilter:ColorMatrixFilter, ConvolutionFilter:ConvolutionFilter, DisplacementMapFilter:DisplacementMapFilter, DropShadowFilter:DropShadowFilter, GlowFilter:GlowFilter, GradientBevelFilter:GradientBevelFilter, GradientGlowFilter:GradientGlowFilter};
			// getter function template
			_getter = {__resolve:function (name:String):Function {
				var f:Function = function ():BitmapFilter {
					var local:Object = this;
					if (local['filters'] != undefined) {
						var spl:Array = name.split("_");
						if (spl[1] == 'blur') {
							spl[1] = 'blurX';
						}
						// new filter not created if missing (getFilterProp can, however) 
						return FuseFMP.getFilter(this, spl[0]+"Filter", false)[spl[1]];
					}
				};
				return f;
			}};
			// setter function template
			_setter = {__resolve:function (name:String):Function {
				var f:Function = function (val:Object) {
					var local:Object = this;
					if (local['filters'] != undefined) {
						FuseFMP.setFilterProp(this, name, val);
					}
				};
				return f;
			}};
		}
		if (arguments[0] == null) {
			return;
		}
		// make sure only filterable targets are initialized! 
		var valid:Array = [MovieClip, Button, TextField];
		for (var i:String in arguments) {
			var ok:Boolean = false;
			for (var j:String in valid) {
				if (arguments[i] instanceof valid[j] || arguments[i] == Function(valid[j]).prototype) {
					ok = true;
					break;
				}
			}
			if (!ok) {
				FuseKitCommon.error('201', i);
				continue;
			}
			for (var filtername:String in _classes) {
				var f:BitmapFilter = new (_classes[filtername])();
				for (var b:String in f) {
					// excluding .clone and any other method encountered
					if (typeof f[b] == 'function') {
						continue;
					}
					var prop:String = filtername.substr(0, -6)+"_"+b;
					(arguments[i]).addProperty(prop, _getter[prop], _setter[prop]);
					// must remain overwritable for direct initialization of individual targets.
					_global.ASSetPropFlags(arguments[i], prop, 3, 1);
					if (b == 'blurX') {
						prop = prop.slice(0, -1);
						(arguments[i]).addProperty(prop, _getter[prop], _setter[prop]);
						_global.ASSetPropFlags(arguments[i], prop, 3, 1);
					}
				}
			}
			for (var s:String in _shortcuts) {
				(arguments[i])[s] = _shortcuts[s];
				_global.ASSetPropFlags(arguments[i], s, 7, 1);
			}
		}
	}
	
	/**
	* Removes shortcuts from specific targets, or from base prototypes if no targets are passed.
	* @param	Accepts one or more MovieClips, TextFields, or Buttons to strip FusFMP shortcuts from, 
	* 			or pass nothing/null to undo {@link #simpleSetup}.
	* @see		#initialize
	*/
	public static function deinitialize():Void {
		if (_classes == undefined) {
			return;
		}
		if (arguments.length == 0) {
			arguments.push(MovieClip.prototype, Button.prototype, TextField.prototype);
		}
		for (var i:String in arguments) {
			for (var filtername:String in _classes) {
				var f:BitmapFilter = new (_classes[filtername])();
				for (var b:String in f) {
					// excluding .clone and any other method encountered
					if (typeof f[b] == 'function') {
						continue;
					}
					var prop:String = filtername.substr(0, -6)+"_"+b;
					// 0,2 is NOT a mistake, do not change
					_global.ASSetPropFlags(arguments[i], prop, 0, 2);
					// safety, might be unnecessary
					(arguments[i]).addProperty(prop, null, null);
					delete (arguments[i])[prop];
				}
			}
			for (var s:String in _shortcuts) {
				// 0,2 is NOT a mistake, do not change
				_global.ASSetPropFlags(arguments[i], s, 0, 2);
				delete (arguments[i])[s];
			}
		}
	}
	
	/**
	* Generic method to get a string version of any filter instance's class name.
	* @description
	* <pre>// list filter in my_mc by name to output window
	* for (var i:String in my_mc.filters) {
	* 	trace( FuseFMP.getFilterName(my_mc.filters[i]) );
	* }</pre>
	* @param instance			BitmapFilter instance
	* @return					BitmapFilter instance name
	* @see #getAllShortcuts
	* @see #traceAllFilters
	*/
	public static function getFilterName(instance:BitmapFilter):String {
		if (_classes == undefined) {
			initialize(null);
		}
		for (var a:String in _classes) {
			if (instance.__proto__ == Function(_classes[a]).prototype) {
				return a;
			}
		}
		return null;
	}
	
	/**
	* Gets current filter index.
	* @param target			A MovieClip, Button or TextField containing the filter
	* @param filter			BitmapFilter instance, String (like "BlurFilter" or "Blur"), or class constructor
	* @return				Index of filter in filters Array or -1 if filter doesn't exist.
	* @see #getFilter
	*/
	public static function getFilterIndex(target:Object, filter:Object):Number {
		if (_classes == undefined) {
			initialize(null);
		}
		filter = getInstance(filter);
		if (filter === null) {
			return -1;
		}
		var ftemp:Array = target.filters;
		for (var i:Number = 0; i<ftemp.length; i++) {
			if ((ftemp[i]).__proto__ == filter.__proto__) {
				return i;
			}
		}
		return -1;
	}
	
	/**
	* Fast getter for a single filter with the option to generate a new filter if missing.
	* @param target				MovieClip, Button or TextField containing the filter
	* @param filter				BitmapFilter instance, String (like "BlurFilter" or "Blur"), or class constructor
	* @param createNew			Pass true to generate a filter during the call if there isn't one
	* @return					A BitmapFilter instance, or null if none found and createNew option not used.
	* @see #getFilterIndex
	*/
	public static function getFilter(target:Object, filter:Object, createNew:Boolean):BitmapFilter {
		var index:Number = getFilterIndex(target, filter);
		if (index == -1) {
			if (createNew != true) {
				return null;
			}
			index = writeFilter(target, filter);
			if (index == -1) {
				return null;
			}
		}
		return (target.filters[index]);
	}
	
	/**
	* Writes a new filter or overwrites an existing filter.
	* @description Use {@link #setFilterProps} to update existing filters - this method overwrites an existing instance.
	* @param target				MovieClip, Button or TextField containing the filter
	* @param filter				BitmapFilter instance, String (like "BlurFilter" or "Blur"), or class constructor
	* @param propsObj			optional, a generic object customizing the new filter, like <code>{blurX:50,quality:1}</code>
	* @return				 	index in target's filters array (or -1 if fails)
	* @see #removeFilter
	* @see #getFilterProp
	* @see #setFilterProp
	* @see #setFilterProps
	*/
	public static function writeFilter(target:Object, filter:Object, propsObj:Object):Number {
		if (_classes == undefined) {
			initialize(null);
		}
		filter = getInstance(filter);
		if (filter === null) {
			return -1;
		}
		var ftemp:Array = target.filters;
		var index:Number = getFilterIndex(target, filter);
		if (index == -1) {
			ftemp.push(filter);
		} else {
			ftemp[index] = filter;
		}
		target.filters = ftemp;
		if (typeof propsObj == 'object') {
			setFilterProps(target, filter, propsObj);
		}
		index = getFilterIndex(target, filter);
		return (index);
	}
	
	/**
	* Clears filter.
	* @param target				MovieClip, Button or TextField containing the filter
	* @param filter				BitmapFilter instance, String (like "BlurFilter" or "Blur"), or class constructor
	* @return					true or false for success.
	* @see #writeFilter
	* @see #getFilterProp
	* @see #setFilterProp
	* @see #setFilterProps
	*/
	public static function removeFilter(target:Object, filter:Object):Boolean {
		if (_classes == undefined) {
			initialize(null);
		}
		filter = getInstance(filter);
		var ftemp:Array = target.filters;
		var index:Number = getFilterIndex(target, filter);
		if (index == -1) {
			return (false);
		}
		ftemp.splice(index, 1);
		target.filters = ftemp;
		return true;
	}
	
	/**
	* Fast getter for a single filter property with option to generate a new filter if missing.
	* @param target				MovieClip, Button or TextField containing the filter
	* @param propname			A single property in FuseFMP notation like 'DropShadow_distance'
	* @param createNew			Pass true to generate a filter during the call if there isn't one
	* @return					the value of the property, usually a number
	* @see #writeFilter
	* @see #getFilterProp
	* @see #setFilterProp
	* @see #setFilterProps
	*/
	public static function getFilterProp(target:Object, propname:String, createNew:Boolean):Object {
		var spl:Array = propname.split("_");
		if (spl[1] == 'blur') {
			spl[1] = 'blurX';
		}
		return (getFilter(target, spl[0]+"Filter", createNew)[spl[1]]);
	}
	
	/**
	* Fast setter for a specific filter property. Generates a new filter if needed.
	* @description	Efficiency: Note that when speed is a priority, {@link #setFilterProps} is faster for setting 
	* batches of 3 or more properties.
	* @usage
	* <pre>FuseFMP.setFilterProp(my_mc, 'Blur_blurX', 50);
	* 
	* FuseFMP.setFilterProp(my_mc, 'GradientBevel_colors', [0xFFFFFF, 0x000000, 0x333333]);</pre>
	* a single value but far faster for batches of 3 or more properties.
	* 
	* @param target			MovieClip, Button or TextField containing the filter to update
	* @param propname		A single property in FuseFMP notation like 'DropShadow_distance'
	* @param value			The value to set the filter property to
	* 
	* @see #getFilterProp
	* @see #setFilterProps
	* @see #writeFilter
	* @see #removeFilter
	*/
	public static function setFilterProp(target:Object, propname:Object, value:Object):Void {
		if (_classes == undefined) {
			initialize(null);
		}
		var spl:Array = propname.split("_");
		var fname:String = (spl[0]+"Filter");
		if (_classes[fname] == undefined) {
			return;
		}
		// avoids calling other methods for speed
		var f:BitmapFilter = (new (_classes[fname])());
		if (BLUR_ZERO==true && fname=='BlurFilter') f['blurX'] = f['blurY'] = 0;
		var prop:String = spl[1];
		var index:Number = (target.filters.length || 0);
		while (--index>-1) {
			if (target.filters[index].__proto__ == f.__proto__) {
				f = target.filters[index]; // use existing
				break;
			}
		}
		if (f==null) {
			FuseKitCommon.error('202',fname,target);
			continue;
		}
		if (prop == 'blur') {
			f['blurX'] = value;
			f['blurY'] = value;
		} else {
			if (typeof value == 'string' && prop.toLowerCase().indexOf('color')>-1) {
				if (value.charAt(0) == '#') value = value.slice(1);
				value = ((value.charAt(1)).toLowerCase() != 'x') ? Number('0x'+value) : Number(value);
			}
			f[prop] = value;
		}
		var ftemp:Array = target.filters;
		if (index == -1) {
			ftemp.push(f);
		} else {
			ftemp[index] = f;
		}
		target.filters = ftemp;
	}
	
	/**
	* Power method that handles multiple targets, props, and filters in one call. Generates new filters as needed.
	* @description This all-purpose method can be used in place of {@link #writeFilter} and {@link #setFilterProp}.
	* @usage
	* There are two distinct syntaxes.
	* <ol>
	* <li>Pass a generic object in the second parameter containing any number of full FuseFMP-notation properties, 
	* thus allowing you to update any number of Filters at once:<br> 
	* <code>FuseFMP.setFilterProps(my_mc, { Blur_blurX:50, Bevel_blurX:1 });</code>.</li>
	* <li>Specify a filter in the second parameter, then pass a generic object in the third parameter containing 
	* normal filter property names:<br>
	* <code>FuseFMP.setFilterProps(my_mc, 'Blur', { blurX:50, blurY:0, quality:3 });</code></li>
	* </ol>
	* For either option you may also pass multiple targets in an array such as:<br>
	* <code>FuseFMP.setFilterProps([my_mc, my_txt], 'Blur', { blur:20 });</code>
	* <br><br>
	* If the filters specified don't exist they are generated automatically.
	* <br><br>
	* Efficiency: Note that when speed is a priority, {@link #setFilterProp} is faster for 1-2 property updates while 
	* this method is much faster for batches of 3 or more properties.
	* 
	* @param target				One or an Array of MovieClips, Buttons or TextFields to update
	* @param filterOrPropsObj	Either a filter (BitmapFilter instance, String like "BlurFilter" or "Blur" or class constructor), 
	* 							or a generic object mapping any number of FuseFMP-formatted property names like Blur_blurX:10.
	* @param propsObj			If the previous parameter specifies a filter: Generic object mapping any number of property names 
	* 							such as distance:10.
	* @see #getFilterProp
	* @see #setFilterProp
	* @see #writeFilter
	* @see #removeFilter
	*/
	public static function setFilterProps(target:Object, filterOrPropsObj:Object, propsObj:Object):Void {
		if (arguments.length<2) {
			FuseKitCommon.error('203', arguments.length);
			return;
		}
		if (_classes == undefined) {
			initialize(null);
		}
		// Build a hash of FilterNames.
		var fo:Object = new Object();
		var multi:Boolean = (arguments.length == 2);
		if (multi==false) {
			var fname:String = getFilterName(getInstance(filterOrPropsObj));
			if (_classes[fname]==undefined) {
				FuseKitCommon.error('204', filterOrPropsObj);
				return;
			}
			fo[fname] = 1;
		}
		else {
			propsObj = filterOrPropsObj;
			for (var prop:String in propsObj) {
				var fname:String = prop.split("_")[0]+"Filter";
				if (_classes[fname]!=undefined && fo[fname]==undefined) {
					fo[fname] = 1;
				}
			}
		}
		// parse and write the filters
		if (!(target instanceof Array)) target = [target];
		for (var i:String in target) {
			var t:Object = target[i];
			for (var fname:String in fo) {
				// avoids calling other methods for speed
				var f:BitmapFilter = (new (_classes[fname])());
				if (BLUR_ZERO==true && fname=='BlurFilter') f['blurX'] = f['blurY'] = 0;
				var index:Number = (t.filters.length || 0);
				while (--index>-1) {
					if (t.filters[index].__proto__ == f.__proto__) {
						f = t.filters[index]; // use existing
						break;
					}
				}
				if (f==null) {
					FuseKitCommon.error('202',fname,t);
					continue;
				}
				var prefix:String = String(fname).slice(0, -6)+'_';
				for (var prop:String in propsObj) {
					var prefixMatches:Boolean = (prop.indexOf(prefix) == 0);
					if (multi==true && prefixMatches==false) {
						continue;
					}
					var value:Object = propsObj[prop];
					if (prefixMatches==true) {
						// trim prefix from propname
						prop = prop.slice(prefix.length);
					}
					if (prop == 'blur') {
						f['blurX'] = value;
						f['blurY'] = value;
					} else {
						if (typeof value == 'string' && prop.toLowerCase().indexOf('color')>-1) {
							if (value.charAt(0) == '#') value = value.slice(1);
							value = ((value.charAt(1)).toLowerCase() != 'x') ? Number('0x'+value) : Number(value);
						}
						f[prop] = value;
					}
				}
				var ftemp:Array = t.filters;
				if (index == -1) {
					ftemp.push(f);
				} else {
					ftemp[index] = f;
				}
				t.filters = ftemp;
			}
		}
	}
	
	/**
	* Retrieves an array of supported property shortcut strings. Similar to traceAllFilters.
	* @return				array of supported property shortcuts
	* @see #traceAllFilters
	* @see #getFilterName
	*/
	public static function getAllShortcuts():Array {
		if (_classes == undefined) {
			initialize(null);
		}
		var fa:Array = [];
		for (var filtername:String in _classes) {
			var f:BitmapFilter = new (_classes[filtername])();
			for (var b:String in f) {
				if (typeof f[b] == 'function') {
					continue;
				}
				fa.push(filtername.substr(0, -6)+'_'+b);
				if (b == 'blurX') {
					fa.push(filtername.substr(0, -6)+'_blur');
				}
			}
		}
		return fa;
	}
	
	/**
	* Lists all filters and shortcut properties (Bevel_blurX, etc.) to the Output panel for reference.
	* @see #getAllShortcuts
	* @see #getFilterName
	*/
	public static function traceAllFilters():Void {
		if (_classes == undefined) {
			initialize(null);
		}
		var s:String = "------ FuseFMP filter properties ------\n";
		for (var filtername:String in _classes) {
			s += (filtername);
			var f:BitmapFilter = new (_classes[filtername])();
			for (var b:String in f) {
				if (typeof f[b] == 'function') {
					continue;
				}
				// excluding .clone and any other method encountered 
				s += ('	- '+filtername.substr(0, -6)+'_'+b);
				if (b == 'blurX') {
					s += ('	- '+filtername.substr(0, -6)+'_blur');
				}
				// show "_blur" props 
			}
			s += '\n';
		}
		FuseKitCommon.output(s);
	}
		
	/**
	* Internal - generates or returns existing BitmapFilter based on input type
	* @param filter		BitmapFilter instance, String (like "BlurFilter" or "Blur"), or class constructor
	*/
	private static function getInstance(filter:Object):BitmapFilter {
		if (filter instanceof BitmapFilter) {
			return BitmapFilter(filter);
		}
		if (typeof filter == "function") {
			for (var j:String in _classes) {
				if (filter == _classes[j]) {
					var f:BitmapFilter = new (_classes[j])();
					if (BLUR_ZERO==true && j=='BlurFilter') f['blurX'] = f['blurY'] = 0;
					return f;
				}
			}
		}
		if (typeof filter == "string") {
			var filterStr:String = String(filter);
			if (filterStr.substr(-6) != 'Filter') filterStr += 'Filter';
			for (var j:String in _classes) {
				if (j == filterStr) {
					var f:BitmapFilter = new (_classes[j])();
					if (BLUR_ZERO==true && j=='BlurFilter') f['blurX'] = f['blurY'] = 0;
					return f;
				}
			}
		}
		return null;
	}
}
